#pragma once

#include <iostream>
#include <memory>
#include <string>

/**
 * 协议定制：
 * 1. 如何序列化-反序列化
 * 2. 解决粘包问题 -> 规定数据边界
 * 
 * req约定：len'\n'x op y'\n'
 * 其中：len为自描述字段，表示有效载荷长度，不含两\n
 *      特殊字符\n用于分隔报头与报文 - 载荷不能保证之中不含\n，len可以
 *      x，y为操作数，op为操作符，三者以空格分隔
 *      最后的\n仅为打印方便
 * resp同理：len'\n'result code'\n'
*/

namespace DLP // Datagram Length Protocol 数据报长度协议
{
    const std::string contentSeparator = " ";
    const std::string dlpSeparator = "\r\n"; //telnet敲回车，是\r\n

    std::string dlpEncoding(const std::string& content)
    {
        std::string len = std::to_string(content.size());
        return len + dlpSeparator + content + dlpSeparator;
    }

    bool dlpDecoding(std::string& dlpByteStream, std::string& content)
    {
        int pos = dlpByteStream.find(dlpSeparator);
        if(pos == std::string::npos) return false;
        std::string lenString = dlpByteStream.substr(0, pos);
        int len = std::stoi(lenString);
        int dlpLength = lenString.size() + len + 2 * dlpSeparator.size();
        if(dlpByteStream.size() < dlpLength) return false;

        //至少包含一份完整报文，输出剩余字节流
        content = dlpByteStream.substr(pos + dlpSeparator.size(), len);
        dlpByteStream.erase(0, dlpLength);
        return true;
    }

    class Request
    {
    public:
        Request()
        {}

        Request(int x, int y, char op)
            : _data_x(x), _data_y(y), _opreation(op)
        {}

        std::string serialize()
        {
            return std::to_string(_data_x) + contentSeparator + 
                _opreation + contentSeparator + 
                std::to_string(_data_y);
        }
        
        bool deserialize(const std::string& content)
        {
            int left = content.find(contentSeparator);
            if(left == std::string::npos) return false;
            int right = content.rfind(contentSeparator);
            if(right == std::string::npos) return false;

            _data_x = std::stoi(content.substr(0, left));
            _data_y = std::stoi(content.substr(right + contentSeparator.size()));
            std::string op = content.substr(left + contentSeparator.size(), right - (left + contentSeparator.size()));
            if(op.size() != 1) return false;
            _opreation = op[0];

            return true;
        }

        int getX()
        {
            return _data_x;
        }

        int getY()
        {
            return _data_y;
        }

        char getOp()
        {
            return _opreation;
        }

        ~Request()
        {}
    private:
        int _data_x;
        int _data_y;
        char _opreation;
    };

    class Response
    {
    public:
        Response()
        {}
        
        Response(int result, int code)
            : _result(result), _code(code)
        {}

        std::string serialize()
        {
            return std::to_string(_result) + contentSeparator + std::to_string(_code);
        }
        
        bool deserialize(const std::string& content)
        {
            int pos = content.find(contentSeparator);
            if(pos == std::string::npos) return false;

            _result = std::stoi(content.substr(0, pos));
            _code = std::stoi(content.substr(pos + contentSeparator.size()));

            return true;
        }

        int getResult()
        {
            return _result;
        }

        int getCode()
        {
            return _code;
        }

        ~Response()
        {}
    private:
        int _result;
        int _code;
    };

    class ReqRespFactory
    {
    public:
        std::shared_ptr<Request> makeRequest()
        {
            return std::make_shared<Request>();
        }

        std::shared_ptr<Request> makeRequest(int x, int y, char op)
        {
            return std::make_shared<Request>(x, y, op);
        }

        std::shared_ptr<Response> makeResponse()
        {
            return std::make_shared<Response>();
        }

        std::shared_ptr<Response> makeResponse(int result, int code)
        {
            return std::make_shared<Response>(result, code);
        }
    };
}

